﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;
using StyleMVVM.DependencyInjection;
using StyleMVVM.Utilities;

namespace StyleMVVM.Validation.Impl
{
	public sealed class AttributeValidationProvider : IValidationRuleProvider
	{
		private static readonly string supplemental = typeof(AttributeValidationProvider).FullName;
		private IReflectionService reflectionService;
		private IGlobalCache globalCache;

		/// <summary>
		/// Register AttributeValidationProvider with DI container.
		/// </summary>
		/// <param name="container">DI container</param>
		public static void RegisterExport(IDependencyInjectionContainer container)
		{
			container.Register(typeof(AttributeValidationProvider))
			         .As(typeof(IValidationRuleProvider))
			         .ImportMethod("ImportDependencies");
		}

		public void ImportDependencies(IReflectionService reflectionService, IGlobalCache globalCache)
		{
			this.reflectionService = reflectionService;
			this.globalCache = globalCache;
		}

		public IEnumerable<IValidationRule> ProvideRules(Type objectType, IEnumerable<string> standards)
		{
			Tuple<List<IValidationRule>, List<string>> validationInfo =
				FetchValidationInformation(objectType, standards);

			return validationInfo.Item1;
		}

		public IEnumerable<string> GetPropertiesToValidate(Type objectType, IEnumerable<string> standards)
		{
			Tuple<List<IValidationRule>, List<string>> validationInfo =
				FetchValidationInformation(objectType, standards);

			return validationInfo.Item2;
		}

		private Tuple<List<IValidationRule>, List<string>> FetchValidationInformation(Type objectType,
		                                                                              IEnumerable<string> standards)
		{
			string key = "AttributeValidationProvider|" + objectType.FullName;
			Tuple<List<IValidationRule>, List<string>>
				returnValue =
					globalCache.GetValue(key) as Tuple<List<IValidationRule>, List<string>>;

			if (returnValue == null)
			{
				List<string> propertyNames = new List<string>();
				List<IValidationRule> validationRulesList = new List<IValidationRule>();

				returnValue = new Tuple<List<IValidationRule>, List<string>>(validationRulesList, propertyNames);

				foreach (PropertyInfo propertyInfo in objectType.GetTypeInfo().DeclaredProperties)
				{
					foreach (Attribute customAttribute in propertyInfo.GetCustomAttributes(true))
					{
						ValidationAttribute validationAttribute = customAttribute as ValidationAttribute;

						if (validationAttribute != null)
						{
							AttributeValidationRule newRule =
								new AttributeValidationRule(validationAttribute, reflectionService, objectType, propertyInfo.Name);

							validationRulesList.Add(newRule);
						}
						else
						{
							IValidatePropertyAttribute validatePropertyAttribute = customAttribute as IValidatePropertyAttribute;

							if (validatePropertyAttribute != null)
							{
								propertyNames.Add(propertyInfo.Name);
							}
						}
					}
				}

				globalCache.AddWithSlidingExpireTime(key, returnValue, new TimeSpan(0, 15, 0));
			}

			return returnValue;
		}
	}
}